package com.jcark.utilscode;

import android.app.Activity;
import android.content.Context;
import android.graphics.Rect;
import android.util.Log;
import android.view.View;
import android.view.ViewGroup;
import android.view.ViewTreeObserver;
import android.view.WindowManager;
import android.view.animation.OvershootInterpolator;
import android.widget.EditText;
import android.widget.FrameLayout;

import com.blankj.utilcode.util.BarUtils;
import com.blankj.utilcode.util.SizeUtils;

import java.util.ArrayList;
import java.util.HashMap;

/**
 * Description:扩展的键盘工具类 <br>
 *
 * @author King.Zi<br>
 * @date 2022/9/5<br>
 * Email: 78688689@qq.com
 */
public class KeyboardExtUtils {
    /**
     * Keyboard到EditText的距离
     */
    public static int sKeyboardEditViewOffset = SizeUtils.dp2px(10);
    public static int sHeight = 100;
    public static int sDecorViewInvisibleHeightPre;
    private static ViewTreeObserver.OnGlobalLayoutListener onGlobalLayoutListener;
    private static final HashMap<View, OnSoftInputChangedListener> listenerMap = new HashMap<>();

    public interface OnSoftInputChangedListener {
        void onSoftInputChanged(int height);
    }

    private KeyboardExtUtils() {
        throw new UnsupportedOperationException("u can't instantiate me...");
    }

    private static int sDecorViewDelta = 0;

    private static int getDecorViewInvisibleHeight(final Activity activity) {
        final View decorView = activity.getWindow().getDecorView();
        if (decorView == null) {
            return sDecorViewInvisibleHeightPre;
        }
        final Rect outRect = new Rect();
        decorView.getWindowVisibleDisplayFrame(outRect);
        int delta = Math.abs(decorView.getBottom() - outRect.bottom);
        if (delta <= BarUtils.getNavBarHeight()) {
            sDecorViewDelta = delta;
            return 0;
        }
        return delta - sDecorViewDelta;
    }

    /**
     * Register soft input changed listener.
     *
     * @param activity The activity.
     * @param listener The soft input changed listener.
     */
    public static void registerSoftInputChangedListener(final Activity activity,
                                                        final OnSoftInputChangedListener listener) {
        final int flags = activity.getWindow().getAttributes().flags;
        if ((flags & WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS) != 0) {
            activity.getWindow().clearFlags(WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS);
        }
        final FrameLayout contentView = activity.findViewById(android.R.id.content);
        sDecorViewInvisibleHeightPre = getDecorViewInvisibleHeight(activity);
        listenerMap.put(contentView, listener);
        onGlobalLayoutListener = () -> {
            int height = getDecorViewInvisibleHeight(activity);
            if (sDecorViewInvisibleHeightPre != height) {
                //通知所有弹窗的监听器输入法高度变化了
                for (OnSoftInputChangedListener changedListener : listenerMap.values()) {
                    changedListener.onSoftInputChanged(height);
                }
                sDecorViewInvisibleHeightPre = height;
            }
        };
        contentView.getViewTreeObserver()
                .addOnGlobalLayoutListener(onGlobalLayoutListener);
    }

    public static void moveUpToKeyboard(int keyboardHeight, Activity activity) {
        FrameLayout content = activity.findViewById(android.R.id.content);
        //content.setBackgroundColor(ContextCompat.getColor(activity,R.color.main_color));
        //找到所有输入框
        ArrayList<EditText> allEts = new ArrayList<>();
        findAllEditText(allEts, content);
        EditText focusEt = null;
        //找到焦点EditText
        for (EditText et : allEts) {
            if (et.isFocused()) {
                focusEt = et;
                break;
            }
        }

        //dy是整个屏幕需要上移的高度, 需要求y1 = 软键盘高度+EditText在屏幕的高度+阈值(键盘距离目标EditText保持的位置)
        int dy = 0;

        int windowHeight = getWindowHeight(activity);
        int focusEtTop = 0;
        int focusBottom = 0;
        if (focusEt != null) {
            int[] locations = new int[2];
            //找到焦点EditText的屏幕位置
            focusEt.getLocationOnScreen(locations);
            focusEtTop = locations[1];
            focusBottom = focusEtTop + focusEt.getMeasuredHeight();
        }
        //屏幕顶点（坐标原点）到 焦点EditText 底部的距离 = focusBottom的值 + keyboardHeight（监听键盘变化的值）+自定义的阈值 - 屏幕的高度 = dy
        int overflowHeight = (focusBottom + keyboardHeight) + sKeyboardEditViewOffset - windowHeight;
        if (overflowHeight < 0) {
            //焦点比较高不考虑
            dy = overflowHeight;
        } else if (overflowHeight >= sKeyboardEditViewOffset) {
            overflowHeight = sKeyboardEditViewOffset;
        } else {
            //当overflowHeight 在（0，KE_VALUE）区间时候
            dy = overflowHeight;
        }

        if (focusEt != null && overflowHeight > 0) {
            dy = overflowHeight;
        }
        //最后计算出来动画的偏移高度 这里需要加一个判断如果EditText特别的高,会因为我们的动画上移屏幕导致光标看不到了.
        //解决方案就是把上移动画 高度最后再修正一下 根据EditText的Top来计算
        int etMinHeight = focusEtTop - BarUtils.getNavBarHeight();//上移的最低距离
        dy = Math.min(etMinHeight, dy);
        //dy<0说明没有需要变化
        if (dy < 0) {
            return;
        }
        content.animate().translationY(-dy)
                .setDuration(200)
                .setInterpolator(new OvershootInterpolator(0))
                .start();
    }

    public static void moveUpToKeyboard(int keyboardHeight, Activity activity, ViewGroup content) {
        //content.setBackgroundColor(ContextCompat.getColor(activity,R.color.main_color));
        //找到所有输入框
        ArrayList<EditText> allEts = new ArrayList<>();
        findAllEditText(allEts, content);
        EditText focusEt = null;
        //找到焦点EditText
        for (EditText et : allEts) {
            if (et.isFocused()) {
                focusEt = et;
                break;
            }
        }

        //dy是整个屏幕需要上移的高度, 需要求y1 = 软键盘高度+EditText在屏幕的高度+阈值(键盘距离目标EditText保持的位置)
        int dy = 0;

        int windowHeight = getWindowHeight(activity);
        int focusEtTop = 0;
        int focusBottom = 0;
        if (focusEt != null) {
            int[] locations = new int[2];
            //找到焦点EditText的屏幕位置
            focusEt.getLocationOnScreen(locations);
            focusEtTop = locations[1];
            focusBottom = focusEtTop + focusEt.getMeasuredHeight();
        }
        //屏幕顶点（坐标原点）到 焦点EditText 底部的距离 = focusBottom的值 + keyboardHeight（监听键盘变化的值）+自定义的阈值 - 屏幕的高度 = dy
        int overflowHeight = (focusBottom + keyboardHeight) + sKeyboardEditViewOffset - windowHeight;
        if (overflowHeight < 0) {
            //焦点比较高不考虑
            dy = overflowHeight;
        } else if (overflowHeight >= sKeyboardEditViewOffset) {
            overflowHeight = sKeyboardEditViewOffset;
        } else {
            //当overflowHeight 在（0，KE_VALUE）区间时候
            dy = overflowHeight;
        }

        if (focusEt != null && overflowHeight > 0) {
            dy = overflowHeight;
        }
        //最后计算出来动画的偏移高度 这里需要加一个判断如果EditText特别的高,会因为我们的动画上移屏幕导致光标看不到了.
        //解决方案就是把上移动画 高度最后再修正一下 根据EditText的Top来计算
        //上移的最低距离
        int etMinHeight = focusEtTop - BarUtils.getNavBarHeight();
        dy = Math.min(etMinHeight, dy);
        //dy<0说明没有需要变化
        if (dy < 0) {
            return;
        }
        content.animate().translationY(-dy)
                .setDuration(200)
                .setInterpolator(new OvershootInterpolator(0))
                .start();
    }

    public static int getWindowHeight(Context context) {
        return ((WindowManager) context.getSystemService(Context.WINDOW_SERVICE)).getDefaultDisplay().getHeight();
    }

    public static void findAllEditText(ArrayList<EditText> list, ViewGroup group) {
        for (int i = 0; i < group.getChildCount(); i++) {
            View v = group.getChildAt(i);
            if (v instanceof EditText && v.getVisibility() == View.VISIBLE) {
                list.add((EditText) v);
            } else if (v instanceof ViewGroup) {
                findAllEditText(list, (ViewGroup) v);
            }
        }
    }

    public static void registerKeyboard(Activity activity, ViewGroup rootView) {
        FrameLayout contentView = activity.findViewById(android.R.id.content);
        registerSoftInputChangedListener(activity,
                height -> {
                    Log.d("onSoftInputChanged", "height =" + height);
                    if (height == 0) { // 说明对话框隐藏
                        //没有变化的软键盘
                        contentView.animate().translationY(0)
                                .setInterpolator(new OvershootInterpolator(0))
                                .setDuration(200).start();
                    } else {
                        //发生了变化的软键盘
                        moveUpToKeyboard(height, activity, rootView);
                    }
                });
    }

    public static void unregisterKeyboard(Activity activity) {
        FrameLayout contentView = activity.findViewById(android.R.id.content);
        if (contentView == null) {
            return;
        }
        contentView.getViewTreeObserver().removeOnGlobalLayoutListener(onGlobalLayoutListener);
        listenerMap.remove(contentView);
    }

    /**
     * 监听键盘显示或隐藏
     *
     * @param rootView 根布局视图
     * @param views    要同步隐藏或显示的控件集合
     */
    public static void onKeyboardVisibleSwitch(View rootView, View... views) {
        rootView.getViewTreeObserver().addOnGlobalLayoutListener(() -> {
            final Rect rect = new Rect();
            //root view可见区域
            rootView.getWindowVisibleDisplayFrame(rect);
            //root View不可视区域高度（被其他View遮挡的区或高度)
            int visibleHeight = rootView.getHeight() - rect.bottom;
            for (View view : views) {
                if (null != view) {
                    view.setVisibility(visibleHeight == 0 ? View.VISIBLE : View.GONE);
                }
            }
        });
    }

    public static void controlKeyboardLayout(final View root, final View scrollToView) {
        root.getViewTreeObserver().addOnGlobalLayoutListener(
                () -> {
                    Rect rect = new Rect();
                    // 获取root在窗体的可视区域
                    root.getWindowVisibleDisplayFrame(rect);
                    // 获取root在窗体的不可视区域高度(被其他View遮挡的区域高度)
                    int rootInvisibleHeight = root.getRootView().getHeight() - rect.bottom;
                    // 若不可视区域高度大于100，则键盘显示
                    if (rootInvisibleHeight > sHeight) {
                        int[] location = new int[2];
                        // 获取scrollToView在窗体的坐标
                        scrollToView.getLocationInWindow(location);
                        // 计算root滚动高度，使scrollToView在可见区域
                        int scrollHeight =
                                (location[1] + scrollToView.getHeight() + sKeyboardEditViewOffset) - rect.bottom;
                        root.scrollTo(0, scrollHeight);
                    } else {
                        // 键盘隐藏
                        root.scrollTo(0, 0);
                    }
                });
    }
}